Skip to content

feat(helm)!: restructure values and deliver config via NGROK_OPERATOR…#829

Open
jonstacks wants to merge 1 commit into
ngrok:mainfrom
jonstacks:helm-values-refactor
Open

feat(helm)!: restructure values and deliver config via NGROK_OPERATOR…#829
jonstacks wants to merge 1 commit into
ngrok:mainfrom
jonstacks:helm-values-refactor

Conversation

@jonstacks

@jonstacks jonstacks commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

What

Restructure the Helm values tree around shared platform config (ngrok.), feature flags (features.), and per-component sections (apiManager, agent, bindingsForwarder), implementing Option 2 of the helm values redesign.

App config now reaches the operator binaries the same way Argo CD does it: every app-config CLI flag reads an NGROK_OPERATOR_* environment variable as its default (CLI flag > env var > built-in default), and the chart injects those variables from per-component ConfigMaps via configMapKeyRef entries with optional: true. Per-component overrides are handled at render time by merging .config over the shared config into that component's ConfigMap. Pods roll on config changes via a checksum/config annotation.

Also:

  • Add a values guard that fails the render with an old->new mapping when pre-v1 values are present, so they are never silently ignored
  • Wire commonAnnotations into every chart resource (was declared but unused)
  • Fix agent/bindings-forwarder ServiceAccount component labels
  • Update helm unittest suites, chainsaw fixtures, deploy targets, specs, migration guide, README/schema, and manifest bundle

How

Largely a claude prompt. Opening in draft until I can test it further.

Breaking Changes

Yes, largely around the helm values as we work towards 1.0. The helm chart attempts to guard against accidental breaking changes by failing if any of the old values are supplied instructing the user to update them

Summary by CodeRabbit

  • New Features

    • Configuration can now be set via NGROK_OPERATOR_* environment variables with precedence: CLI flags > environment variables > built-in defaults.
  • Configuration Changes

    • Helm chart values reorganized: shared configuration now nested under ngrok.* and features.*; component-specific settings moved under apiManager, agent, and bindingsForwarder. Migration guide available.
  • Documentation

    • Updated configuration documentation and Helm chart README to reflect new values structure.

@jonstacks jonstacks self-assigned this Jun 12, 2026
@jonstacks jonstacks added the breaking-change Introduces a breaking change label Jun 12, 2026
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds environment-backed CLI defaults, restructures Helm values under shared and component-scoped keys, moves component runtime config into ConfigMaps plus env injection, updates chart templates and tests to the new paths, and refreshes migration and uninstall documentation.

Changes

Configuration model migration

Layer / File(s) Summary
Env-backed command defaults
internal/env/*, cmd/agent-manager.go, cmd/api-manager.go, cmd/bindings-forwarder-manager.go, go.mod
Adds env helpers for string, bool, slice, and zap defaults, tests their precedence rules, and wires the three manager commands to use environment-derived flag defaults.
Helm values model and migration
helm/ngrok-operator/values.yaml, helm/ngrok-operator/values.schema.json, helm/ngrok-operator/README.md, helm/ngrok-operator/templates/_helpers.tpl, helm/ngrok-operator/templates/validate-values.yaml, helm/ngrok-operator/tests/validate-values_test.yaml, specs/..., docs/uninstall.md, tests/chainsaw-uninstall/*, tools/make/*
Reorganizes chart values into ngrok.*, features.*, apiManager.*, agent.*, and bindingsForwarder.*, adds legacy-value validation, and updates docs, fixtures, and make targets to the new paths.
Component config rendering and injection
helm/ngrok-operator/templates/{agent,api-manager,bindings-forwarder}/*, helm/ngrok-operator/tests/{agent,api-manager,bindings-forwarder}/*, manifest-bundle.yaml
Builds shared-plus-component config maps, injects selected keys into Deployments via configMapKeyRef, adds config checksums, reduces CLI args, and updates deployment/configmap tests for the new wiring.
Metadata, feature paths, and RBAC rewiring
helm/ngrok-operator/templates/NOTES.txt, helm/ngrok-operator/templates/ingress-class.yaml, helm/ngrok-operator/templates/**/{role,rolebinding,serviceaccount,pdb}.yaml, helm/ngrok-operator/templates/rbac/crd-access/*, helm/ngrok-operator/tests/*rbac*_test.yaml, helm/ngrok-operator/tests/ingress-class_test.yaml
Switches remaining templates to feature-scoped value paths, merges commonAnnotations into generated resources, updates service account scoping and labels, and aligns RBAC, PDB, ingress class, and notes tests with the new structure.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~100 minutes

Suggested labels

area/devenv

Suggested reviewers

  • alex-bezek

Poem

🐇 I tucked new defaults in env-lit burrows,
And sorted Helm keys into neater furrows.
ConfigMaps now carry the midnight mail,
While old paths politely hop off the trail.
Sniff, patch, render — what a chartful spring!

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

@github-actions github-actions Bot added documentation Improvements or additions to documentation area/controller Issues dealing with the controller area/helm-chart Issues dealing with the helm chart size/XXL Denotes a PR that changes 1000+ lines labels Jun 12, 2026
@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 48.43750% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.33%. Comparing base (6b7dbf7) to head (8c7fdd9).

Files with missing lines Patch % Lines
cmd/api-manager.go 0.00% 20 Missing ⚠️
cmd/agent-manager.go 0.00% 11 Missing ⚠️
cmd/bindings-forwarder-manager.go 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #829      +/-   ##
==========================================
+ Coverage   52.30%   52.33%   +0.02%     
==========================================
  Files         103      104       +1     
  Lines       11379    11413      +34     
==========================================
+ Hits         5952     5973      +21     
- Misses       4975     4985      +10     
- Partials      452      455       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…_* env vars

Restructure the Helm values tree around shared platform config (ngrok.*),
feature flags (features.*), and per-component sections (apiManager, agent,
bindingsForwarder), implementing Option 2 of the helm values redesign.

App config now reaches the operator binaries the same way Argo CD does it:
every app-config CLI flag reads an NGROK_OPERATOR_* environment variable as
its default (CLI flag > env var > built-in default), and the chart injects
those variables from per-component ConfigMaps via configMapKeyRef entries
with optional: true. Per-component overrides are handled at render time by
merging <component>.config over the shared config into that component's
ConfigMap. Pods roll on config changes via a checksum/config annotation.

Also:
- Add a values guard that fails the render with an old->new mapping when
  pre-v1 values are present, so they are never silently ignored
- Wire commonAnnotations into every chart resource (was declared but unused)
- Fix agent/bindings-forwarder ServiceAccount component labels
- Update helm unittest suites, chainsaw fixtures, deploy targets, specs,
  migration guide, README/schema, and manifest bundle

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Jonathan Stacks <jonstacks@users.noreply.github.com>
@jonstacks jonstacks force-pushed the helm-values-refactor branch from 5f14e5c to 8c7fdd9 Compare June 17, 2026 19:58
@jonstacks jonstacks marked this pull request as ready for review June 17, 2026 19:59
@jonstacks jonstacks requested a review from a team as a code owner June 17, 2026 19:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
specs/migration-v1.md (1)

30-57: ⚡ Quick win

Clarify commonAnnotations behavior change in migration guide.

Line 54 lists commonAnnotations under "Unchanged" (value path correct), but the PR objective states these are now "wired into all chart resources (previously declared but unused)". The path is unchanged, but the runtime behavior is new. Users migrating might assume no change is needed; explicitly noting that the path stays the same but the feature is now active would reduce confusion.

Consider clarifying: commonAnnotations (path unchanged, but now wired into all resources).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specs/migration-v1.md` around lines 30 - 57, The migration guide lists
commonAnnotations in the "Unchanged" section (around line 54), but this is
misleading because while the configuration path remains the same, the runtime
behavior has changed—commonAnnotations is now actively wired into all chart
resources instead of being previously unused and ignored. Update the
commonAnnotations entry in the "Unchanged" section to clarify that the path is
unchanged but the feature behavior is new, using wording similar to:
commonAnnotations (path unchanged, but now wired into all resources). This
prevents users from assuming no action or consideration is needed when
migrating.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@helm/ngrok-operator/templates/_helpers.tpl`:
- Around line 135-141: The ngrok-operator.kvPairs template function iterates
over map keys in non-deterministic order, causing identical input values to
render differently and trigger unnecessary ConfigMap checksum changes and pod
rollouts. To fix this, modify the range operation in the ngrok-operator.kvPairs
function to sort the keys before iterating over them, ensuring the output order
is always consistent regardless of the input map's internal ordering. This will
guarantee deterministic rendering of the key-value pairs used in ConfigMap
configurations.

In `@helm/ngrok-operator/templates/rbac/crd-access/domain-editor.yaml`:
- Around line 9-10: In the domain-editor.yaml file where the annotations
variable is defined, reverse the order of the merge arguments for the
crdAccessRoles and commonAnnotations to ensure correct precedence. Currently,
commonAnnotations is merged last (giving it higher priority), but it should be
merged first with crdAccessRoles merged afterward. This ensures that
role-specific annotations in crdAccessRoles override the common base annotations
when keys conflict, following the expected specific-over-general precedence
pattern.

In `@helm/ngrok-operator/values.schema.json`:
- Around line 97-110: The schema validation for the "level", "format", and
"stacktraceLevel" fields documents their allowed values in descriptions but does
not enforce them with constraints, allowing invalid configurations to pass
validation. Add an `enum` constraint to each field: for "level" add enum with
values debug, info, and error; for "format" add enum with values console and
json; for "stacktraceLevel" add enum with values info and error. Ensure these
enum constraints match the allowed values documented in each field's
description. Also apply the same pattern to the similar fields mentioned at
lines 234-243.
- Around line 350-354: The schema default for the maxUnavailable field within
apiManager.podDisruptionBudget is set to an empty string ("\"\""), but the
actual default in values.yaml is "1". Update the default value in the schema
file for the maxUnavailable property to match the chart's actual default of "1"
to ensure consistency between the schema documentation and the runtime
configuration.

---

Nitpick comments:
In `@specs/migration-v1.md`:
- Around line 30-57: The migration guide lists commonAnnotations in the
"Unchanged" section (around line 54), but this is misleading because while the
configuration path remains the same, the runtime behavior has
changed—commonAnnotations is now actively wired into all chart resources instead
of being previously unused and ignored. Update the commonAnnotations entry in
the "Unchanged" section to clarify that the path is unchanged but the feature
behavior is new, using wording similar to: commonAnnotations (path unchanged,
but now wired into all resources). This prevents users from assuming no action
or consideration is needed when migrating.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c6ca502f-2603-4d58-b534-ecaf5ec433cc

📥 Commits

Reviewing files that changed from the base of the PR and between 6b7dbf7 and 8c7fdd9.

⛔ Files ignored due to path filters (10)
  • helm/ngrok-operator/tests/__snapshot__/agent-config-cm_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/__snapshot__/binding-configuration_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/agent/__snapshot__/deployment_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/agent/__snapshot__/service-account_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/agent/__snapshot__/serviceaccount_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/api-manager/__snapshot__/configmap_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/api-manager/__snapshot__/deployment_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/bindings-forwarder/__snapshot__/deployment_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/bindings-forwarder/__snapshot__/service-account_test.yaml.snap is excluded by !**/*.snap
  • helm/ngrok-operator/tests/bindings-forwarder/__snapshot__/serviceaccount_test.yaml.snap is excluded by !**/*.snap
📒 Files selected for processing (88)
  • cmd/agent-manager.go
  • cmd/api-manager.go
  • cmd/bindings-forwarder-manager.go
  • docs/uninstall.md
  • go.mod
  • helm/ngrok-operator/README.md
  • helm/ngrok-operator/templates/NOTES.txt
  • helm/ngrok-operator/templates/_helpers.tpl
  • helm/ngrok-operator/templates/agent/configmap.yaml
  • helm/ngrok-operator/templates/agent/deployment.yaml
  • helm/ngrok-operator/templates/agent/release-namespace-role.yaml
  • helm/ngrok-operator/templates/agent/role.yaml
  • helm/ngrok-operator/templates/agent/rolebinding.yaml
  • helm/ngrok-operator/templates/agent/serviceaccount.yaml
  • helm/ngrok-operator/templates/api-manager/bindings-cluster-role.yaml
  • helm/ngrok-operator/templates/api-manager/configmap.yaml
  • helm/ngrok-operator/templates/api-manager/deployment.yaml
  • helm/ngrok-operator/templates/api-manager/leader-election-role.yaml
  • helm/ngrok-operator/templates/api-manager/pdb.yaml
  • helm/ngrok-operator/templates/api-manager/release-namespace-role.yaml
  • helm/ngrok-operator/templates/api-manager/role.yaml
  • helm/ngrok-operator/templates/api-manager/rolebinding.yaml
  • helm/ngrok-operator/templates/api-manager/serviceaccount.yaml
  • helm/ngrok-operator/templates/bindings-forwarder/configmap.yaml
  • helm/ngrok-operator/templates/bindings-forwarder/deployment.yaml
  • helm/ngrok-operator/templates/bindings-forwarder/role.yaml
  • helm/ngrok-operator/templates/bindings-forwarder/serviceaccount.yaml
  • helm/ngrok-operator/templates/cleanup-hook/job.yaml
  • helm/ngrok-operator/templates/cleanup-hook/rbac.yaml
  • helm/ngrok-operator/templates/credentials-secret.yaml
  • helm/ngrok-operator/templates/ingress-class.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/agentendpoint-editor.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/agentendpoint-viewer.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/boundendpoint-editor.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/boundendpoint-viewer.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/cloudendpoint-editor.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/cloudendpoint-viewer.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/domain-editor.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/domain-viewer.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/ippolicy-editor.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/ippolicy-viewer.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/kubernetesoperator-editor.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/kubernetesoperator-viewer.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/ngroktrafficpolicy-editor.yaml
  • helm/ngrok-operator/templates/rbac/crd-access/ngroktrafficpolicy-viewer.yaml
  • helm/ngrok-operator/templates/validate-values.yaml
  • helm/ngrok-operator/tests/agent/configmap_test.yaml
  • helm/ngrok-operator/tests/agent/deployment_test.yaml
  • helm/ngrok-operator/tests/agent/rbac_test.yaml
  • helm/ngrok-operator/tests/api-manager/configmap_test.yaml
  • helm/ngrok-operator/tests/api-manager/deployment_test.yaml
  • helm/ngrok-operator/tests/api-manager/pdb_test.yaml
  • helm/ngrok-operator/tests/api-manager/rbac_test.yaml
  • helm/ngrok-operator/tests/api-manager/serviceaccount_test.yaml
  • helm/ngrok-operator/tests/bindings-forwarder/configmap_test.yaml
  • helm/ngrok-operator/tests/bindings-forwarder/deployment_test.yaml
  • helm/ngrok-operator/tests/bindings-forwarder/rbac_test.yaml
  • helm/ngrok-operator/tests/bindings-forwarder/serviceaccount_test.yaml
  • helm/ngrok-operator/tests/ingress-class_test.yaml
  • helm/ngrok-operator/tests/validate-values_test.yaml
  • helm/ngrok-operator/values.schema.json
  • helm/ngrok-operator/values.yaml
  • internal/env/env.go
  • internal/env/env_test.go
  • manifest-bundle.yaml
  • specs/helm/agent.md
  • specs/helm/bindings-forwarder.md
  • specs/helm/common.md
  • specs/helm/operator.md
  • specs/migration-v1.md
  • tests/chainsaw-uninstall/_fixtures/values-base.yaml
  • tests/chainsaw-uninstall/_fixtures/values-bindings-enabled.yaml
  • tests/chainsaw-uninstall/bindings-delete-policy/values.yaml
  • tests/chainsaw-uninstall/bindings-retain-policy/values.yaml
  • tests/chainsaw-uninstall/delete-policy-bundled-crds/values.yaml
  • tests/chainsaw-uninstall/delete-policy-separate-crds/values.yaml
  • tests/chainsaw-uninstall/multi-ingressclass-delete-policy/values-operator-a.yaml
  • tests/chainsaw-uninstall/multi-ingressclass-delete-policy/values-operator-b.yaml
  • tests/chainsaw-uninstall/multi-ingressclass-retain-policy/values-operator-a.yaml
  • tests/chainsaw-uninstall/multi-ingressclass-retain-policy/values-operator-b.yaml
  • tests/chainsaw-uninstall/multi-ns-delete-policy/values-operator-a.yaml
  • tests/chainsaw-uninstall/multi-ns-delete-policy/values-operator-b.yaml
  • tests/chainsaw-uninstall/multi-ns-retain-policy/values-operator-a.yaml
  • tests/chainsaw-uninstall/multi-ns-retain-policy/values-operator-b.yaml
  • tests/chainsaw-uninstall/retain-policy-bundled-crds/values.yaml
  • tests/chainsaw-uninstall/retain-policy-separate-crds/values.yaml
  • tools/make/_common.mk
  • tools/make/deploy.mk

Comment on lines +135 to +141
{{- define "ngrok-operator.kvPairs" -}}
{{- $pairs := list -}}
{{- range $key, $value := . -}}
{{- $pairs = append $pairs (printf "%s=%v" $key $value) -}}
{{- end -}}
{{- $pairs | join "," -}}
{{- end -}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Verify current helper body and confirm whether map keys are sorted.
rg -n -A10 -B2 'define "ngrok-operator.kvPairs"' helm/ngrok-operator/templates/_helpers.tpl
# Expected after fix: usage of `keys` and `sortAlpha` before indexing values.

Repository: ngrok/ngrok-operator

Length of output: 530


🏁 Script executed:

rg -n 'kvPairs' helm/ngrok-operator/templates/ -A3 -B3

Repository: ngrok/ngrok-operator

Length of output: 3292


🌐 Web query:

Helm sprig functions sortAlpha keys

💡 Result:

In Helm templates, dictionary keys are inherently unordered [1][2]. Because iterating over a dictionary in a template can result in non-deterministic output, it is a best practice to extract the keys into a list and sort them using the sprig function sortAlpha [3][4][2]. The sortAlpha function sorts a list of strings into alphabetical (lexicographical) order [5][6]. It does not modify the list in place but returns a new, sorted copy [6]. To iterate over a dictionary in a deterministic order, you can use the following pattern: {{- range $key:= keys.Values.myDict | sortAlpha }} {{ $key }}: {{ get $.Values.myDict $key }} {{- end }} In this example: 1. keys returns a list of all keys from the dictionary [1][7]. 2. sortAlpha sorts that list alphabetically [5][6]. 3. The range loop iterates over the sorted keys, and get is used to retrieve the corresponding value from the dictionary for each key [4]. If you are merging multiple dictionaries, you can chain the uniq function to ensure the list of keys is both unique and sorted [1][7][2]: {{- range $key:= keys $dict1 $dict2 | uniq | sortAlpha }}... {{- end }}

Citations:


🏁 Script executed:

# Verify that 'index' function is available in Helm/Sprig
web_search "Helm sprig index function map"

Repository: ngrok/ngrok-operator

Length of output: 115


🌐 Web query:

Helm Sprig index function dictionary retrieval

💡 Result:

In Helm templates, the index function is the standard, built-in Go template function used to retrieve values from a dictionary (map) using a dynamic key [1][2][3]. Although the Helm documentation often emphasizes its use for lists (slices), it is fully supported for dictionary retrieval [4][5][6]. Basic Usage To retrieve a value from a dictionary using a dynamic key, use the following syntax [3]: {{ index.MapVariable "keyName" }} In this example,.MapVariable is the dictionary and "keyName" is the key you wish to access. You can also pass a variable instead of a string literal for the key [3]: {{- $myKey:= "someKey" -}} {{ index.Values.myMap $myKey }} Nested Retrieval The index function supports multi-level lookups by passing additional arguments [7][5][2]. For example, to access a nested value: {{ index.Values "level1" "level2" "targetKey" }} Handling Missing Keys If the key does not exist, the index function returns nil [8][2]. This can cause template execution to fail if you attempt to perform further operations (like accessing sub-keys) on that nil value [8][2]. A common pattern to handle this is to use the default function with an empty dictionary (dict) to ensure a safe fallback [8][2]: {{- $subDict:= index.Values "maybeMissing" | default dict -}} {{- $value:= index $subDict "targetKey" -}} Alternative Functions While index is the primary tool for dynamic retrieval, the Sprig library provides additional dictionary functions that may be useful depending on your needs: - get: Similar to index, but specifically designed for dictionaries [9]. - dig: Useful for traversing nested dictionaries safely, as it allows for a default value if any key in the path is missing [9]. - pluck: Useful if you have a key and multiple dictionaries and want to retrieve the value from whichever dictionary contains that key [9][7].

Citations:


Sort keys in ngrok-operator.kvPairs to keep rendered config deterministic.

Map iteration here is non-deterministic, causing identical values to render in different orders and trigger unnecessary checksum/config changes and pod rollouts. This happens at lines 172, 189, and 190 where the output feeds into ConfigMap configurations.

Suggested fix
 {{- define "ngrok-operator.kvPairs" -}}
 {{- $pairs := list -}}
-{{- range $key, $value := . -}}
-{{- $pairs = append $pairs (printf "%s=%v" $key $value) -}}
+{{- $m := . -}}
+{{- range $key := keys $m | sortAlpha -}}
+{{- $pairs = append $pairs (printf "%s=%v" $key (index $m $key)) -}}
 {{- end -}}
 {{- $pairs | join "," -}}
 {{- end -}}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- define "ngrok-operator.kvPairs" -}}
{{- $pairs := list -}}
{{- range $key, $value := . -}}
{{- $pairs = append $pairs (printf "%s=%v" $key $value) -}}
{{- end -}}
{{- $pairs | join "," -}}
{{- end -}}
{{- define "ngrok-operator.kvPairs" -}}
{{- $pairs := list -}}
{{- $m := . -}}
{{- range $key := keys $m | sortAlpha -}}
{{- $pairs = append $pairs (printf "%s=%v" $key (index $m $key)) -}}
{{- end -}}
{{- $pairs | join "," -}}
{{- end -}}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/ngrok-operator/templates/_helpers.tpl` around lines 135 - 141, The
ngrok-operator.kvPairs template function iterates over map keys in
non-deterministic order, causing identical input values to render differently
and trigger unnecessary ConfigMap checksum changes and pod rollouts. To fix
this, modify the range operation in the ngrok-operator.kvPairs function to sort
the keys before iterating over them, ensuring the output order is always
consistent regardless of the input map's internal ordering. This will guarantee
deterministic rendering of the key-value pairs used in ConfigMap configurations.

Comment on lines +9 to +10
{{- $annotations := merge (dict) (.Values.crdAccessRoles.annotations | default dict) (.Values.commonAnnotations | default dict) }}
{{- with $annotations }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reverse the merge order for correct annotation precedence.

The current merge order causes commonAnnotations to override crdAccessRoles.annotations when keys conflict. This inverts the expected specific-over-general precedence: role-specific annotations should override common base annotations, not the other way around.

In Helm's merge, later arguments take precedence. The correct order should merge common annotations first, then role-specific annotations:

🔧 Proposed fix
-  {{- $annotations := merge (dict) (.Values.crdAccessRoles.annotations | default dict) (.Values.commonAnnotations | default dict) }}
+  {{- $annotations := merge (dict) (.Values.commonAnnotations | default dict) (.Values.crdAccessRoles.annotations | default dict) }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- $annotations := merge (dict) (.Values.crdAccessRoles.annotations | default dict) (.Values.commonAnnotations | default dict) }}
{{- with $annotations }}
{{- $annotations := merge (dict) (.Values.commonAnnotations | default dict) (.Values.crdAccessRoles.annotations | default dict) }}
{{- with $annotations }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/ngrok-operator/templates/rbac/crd-access/domain-editor.yaml` around
lines 9 - 10, In the domain-editor.yaml file where the annotations variable is
defined, reverse the order of the merge arguments for the crdAccessRoles and
commonAnnotations to ensure correct precedence. Currently, commonAnnotations is
merged last (giving it higher priority), but it should be merged first with
crdAccessRoles merged afterward. This ensures that role-specific annotations in
crdAccessRoles override the common base annotations when keys conflict,
following the expected specific-over-general precedence pattern.

Comment on lines +97 to +110
"level": {
"type": "string",
"description": "The level to log at. One of 'debug', 'info', or 'error'.",
"default": "info"
},
"format": {
"type": "string",
"description": "The log format to use. One of console, json.",
"default": "json"
},
"stacktraceLevel": {
"type": "string",
"description": "The level to report stacktrace logs one of 'info' or 'error'.",
"default": "error"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Enforce documented allowed values with enum constraints.

These fields describe fixed allowed values but currently accept arbitrary strings, so invalid configs pass schema validation and fail later at runtime/behavior level.

Suggested fix
                         "level": {
                             "type": "string",
                             "description": "The level to log at. One of 'debug', 'info', or 'error'.",
+                            "enum": ["debug", "info", "error"],
                             "default": "info"
                         },
                         "format": {
                             "type": "string",
                             "description": "The log format to use. One of console, json.",
+                            "enum": ["console", "json"],
                             "default": "json"
                         },
                         "stacktraceLevel": {
                             "type": "string",
                             "description": "The level to report stacktrace logs one of 'info' or 'error'.",
+                            "enum": ["info", "error"],
                             "default": "error"
                         }
...
                 "drainPolicy": {
                     "type": "string",
                     "description": "Policy for what to do with ngrok API resources while draining during an Uninstall. \"Delete\" removes ngrok API resources, \"Retain\" preserves them",
+                    "enum": ["Delete", "Retain"],
                     "default": "Retain"
                 },
                 "defaultDomainReclaimPolicy": {
                     "type": "string",
                     "description": "The default domain reclaim policy to use for domains created by the operator. Valid values are \"Delete\" and \"Retain\". The default is \"Delete\".",
+                    "enum": ["Delete", "Retain"],
                     "default": "Delete"
                 }

Also applies to: 234-243

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/ngrok-operator/values.schema.json` around lines 97 - 110, The schema
validation for the "level", "format", and "stacktraceLevel" fields documents
their allowed values in descriptions but does not enforce them with constraints,
allowing invalid configurations to pass validation. Add an `enum` constraint to
each field: for "level" add enum with values debug, info, and error; for
"format" add enum with values console and json; for "stacktraceLevel" add enum
with values info and error. Ensure these enum constraints match the allowed
values documented in each field's description. Also apply the same pattern to
the similar fields mentioned at lines 234-243.

Comment on lines +350 to 354
"maxUnavailable": {
"type": "string",
"description": "The name of the ServiceAccount to use for the agent.",
"default": ""
"description": "Maximum number/percentage of pods that may be made unavailable",
"default": "\"\""
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align schema default with chart default for apiManager.podDisruptionBudget.maxUnavailable.

Schema default is \"\", while helm/ngrok-operator/values.yaml defaults this to "1". This mismatch is already surfacing in generated docs and can mislead users.

Suggested fix
                         "maxUnavailable": {
                             "type": "string",
                             "description": "Maximum number/percentage of pods that may be made unavailable",
-                            "default": "\"\""
+                            "default": "1"
                         },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"maxUnavailable": {
"type": "string",
"description": "The name of the ServiceAccount to use for the agent.",
"default": ""
"description": "Maximum number/percentage of pods that may be made unavailable",
"default": "\"\""
},
"maxUnavailable": {
"type": "string",
"description": "Maximum number/percentage of pods that may be made unavailable",
"default": "1"
},
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/ngrok-operator/values.schema.json` around lines 350 - 354, The schema
default for the maxUnavailable field within apiManager.podDisruptionBudget is
set to an empty string ("\"\""), but the actual default in values.yaml is "1".
Update the default value in the schema file for the maxUnavailable property to
match the chart's actual default of "1" to ensure consistency between the schema
documentation and the runtime configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/controller Issues dealing with the controller area/helm-chart Issues dealing with the helm chart breaking-change Introduces a breaking change documentation Improvements or additions to documentation size/XXL Denotes a PR that changes 1000+ lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant